package jmine.tec.hydra.web.pages.upload.files;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import jmine.tec.hydra.HydraController;
import jmine.tec.hydra.api.HydraSpecification;
import jmine.tec.hydra.dao.HydraSpecificationDAO;
import jmine.tec.hydra.domain.HydraRequestImpl;
import jmine.tec.hydra.domain.specification.HydraSpecificationImpl;
import jmine.tec.hydra.web.HydraWebMessages;
import jmine.tec.hydra.web.pages.requisicao.ConsultaDetalheIntegracaoRequisicao;
import jmine.tec.report.impl.ReportBuilder;
import jmine.tec.utils.UnmodifiableTuple;
import jmine.tec.web.wicket.component.button.ActionButton;
import jmine.tec.web.wicket.component.command.button.ButtonCommand;
import jmine.tec.web.wicket.component.table.SearchResultSelectionTable;
import jmine.tec.web.wicket.pages.form.ExecutePage;
import jmine.tec.web.wicket.spring.SpringBeanReference;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.util.ListModel;
import org.apache.wicket.spring.injection.annot.SpringBean;

import bancosys.tec.exception.LocalizedMessageHolder;
import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.dao.BaseDAO;
import bancosys.tec.persist.dao.BaseDAOFactory;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.persist.validator.ValidationException;
import bancosys.tec.utils.date.Date;

/**
 * @author icaro
 * @param <M> model
 * @param <BO> entidade
 */
public abstract class AbstractUploadFile<M, BO extends Persistable> extends ExecutePage<M, BO> {

    @SpringBean(name = "integrationRecords")
    private IntegrationRecords integrationRecords;

    private final SpringBeanReference<HydraController> hydraControllerRef = SpringBeanReference.forName("hydraController");

    private HydraSpecificationImpl specification;

    private Set<Serializable> chosen;

    private Date dataRef = new Date();

    /**
     * Construtor
     */
    public AbstractUploadFile() {
        super();
    }

    /**
     * Construtor
     * 
     * @param pageParameters {@link PageParameters}
     */
    public AbstractUploadFile(PageParameters pageParameters) {
        super(pageParameters);
    }

    /**
     * {@inheritDoc}
     */
    public List<BO> search(BaseDAOFactory baseDaoFactory) {
        BaseDAO<BO> dao = baseDaoFactory.getGenericDAO(this.getEntityClass());
        return dao.findAll();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void addSearchFields(WebMarkupContainer container) {
        container.add(this.createDateField("dataRef", new PropertyModel<Date>(this, "dataRef")).setRequired(true));
        container.add(this.createHydraScheduledSpecificatioSelect());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Panel createSearchResultPanel(String id, ReportBuilder reportBuilder) {
        SearchResultSelectionTable<BO> searchResultSelectionTable =
                new SearchResultSelectionTable<BO>(id, reportBuilder.createReport(), this);
        this.chosen = searchResultSelectionTable.getChosen();
        return searchResultSelectionTable;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<ButtonCommand> getPageCommands() {
        List<ButtonCommand> commands = new ArrayList<ButtonCommand>();
        commands.add(new SendButtonCommand());
        return commands;
    }

    /**
     * Creates a select list with the relevant specifications.
     * 
     * @return um {@link DropDownChoice} com as especificações.
     */
    private DropDownChoice<HydraSpecification> createHydraScheduledSpecificatioSelect() {
        List<HydraSpecification> specs = this.integrationRecords.getSpecifications();
        ListModel<HydraSpecification> listModel = new ListModel<HydraSpecification>(specs);
        DropDownChoice<HydraSpecification> select =
                new DropDownChoice<HydraSpecification>("specification", listModel, new ChoiceRenderer<HydraSpecification>("action"));
        select.setDefaultModel(new PropertyModel<HydraSpecification>(this, "specification"));
        select.setOutputMarkupId(true);
        select.setNullValid(true);
        return select;
    }

    /**
     * Uploads the selected integration time and with the roles chosen.
     */
    private void doEnviar() {
        List<BO> boSelecionados = this.getBOSelecionados();
        List<LocalizedMessageHolder> erros = this.verificaBO();
        if (boSelecionados.isEmpty()) {
            this.error(HydraWebMessages.SELECT_AT_LEAST_ONE_RECORD.create());
        } else if (!erros.isEmpty()) {
            for (LocalizedMessageHolder erro : erros) {
                this.error(erro);
            }
        } else {
            UnmodifiableTuple<Date, List<BO>> source = new UnmodifiableTuple<Date, List<BO>>(this.dataRef, boSelecionados);
            try {
                this.specification = this.getSpecificationDAO().findByPk(this.specification.getId());
            } catch (BeanNotFoundException e) {
                this.error(e.getMessage());
            }
            HydraRequestImpl request = (HydraRequestImpl) this.hydraControllerRef.get().send(source, this.specification);
            this.setResponsePage(new ConsultaDetalheIntegracaoRequisicao(request.getPk(), this.page()));
        }
    }

    /**
     * @return the implementation of {@link AbstractUploadFile}
     */
    protected abstract AbstractUploadFile page();

    /**
     * Makes the necessary validations and returns errors
     * 
     * @return lista de {@link LocalizedMessageHolder}
     */
    protected abstract List<LocalizedMessageHolder> verificaBO();

    /**
     * @return a lista de {@link BO} selecionados.
     */
    protected List<BO> getBOSelecionados() {
        List<BO> boSelecionados = new ArrayList<BO>();

        for (Serializable pk : this.chosen) {
            try {
                boSelecionados.add(this.loadEntity(pk));
            } catch (BeanNotFoundException e) {
                this.error(e.getMessage());
            }
        }

        return boSelecionados;
    }

    /**
     * @return o dao de HydraScheduledSpecification
     */
    private HydraSpecificationDAO getSpecificationDAO() {
        return this.daoFactoryRef.get().getDAOByClass(HydraSpecificationDAO.class);
    }

    /**
     * @return the dataRef
     */
    public Date getDataRef() {
        return this.dataRef;
    }

    /**
     * @param dataRef the dataRef to set
     */
    public void setDataRef(Date dataRef) {
        this.dataRef = dataRef;
    }

    /**
     * Button is responsible for processing file uploads.
     * 
     * @author Rafael Volpato
     * @date Oct 1, 2011
     */
    private class SendButtonCommand implements ButtonCommand {

        /**
         * {@inheritDoc}
         */
        public String getLabel() {
            return HydraWebMessages.SEND.create().getMessage();
        }

        /**
         * {@inheritDoc}
         */
        public Button createButtonComponent(String id) {
            return new ActionButton(id, new Model<String>(this.getLabel())) {

                @Override
                protected void onAction() throws Exception {
                    try {
                        if (AbstractUploadFile.this.specification != null) {
                            AbstractUploadFile.this.doEnviar();
                        } else {
                            AbstractUploadFile.this.error(HydraWebMessages.SELECT_SPECIFICATION.create());
                        }
                    } catch (ValidationException e) {
                        AbstractUploadFile.this.error(e.getMessage());
                    }
                }
            };
        }
    }
}
